#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <string>

#include <QDebug>
#include "volumecontroller.h"

#include "qkeymapper_constants.h"

using namespace QKeyMapperConstants;

// QKeyMapper-specific GUID for volume change event context
// Generated GUID: {B8A9F2E5-1D4C-4A7B-9F3E-2C8B5A9D7E6F}
// This helps identify volume changes made by QKeyMapper vs other applications
const GUID VolumeController::s_QKeyMapperVolumeGUID =
    { 0xB8A9F2E5, 0x1D4C, 0x4A7B, { 0x9F, 0x3E, 0x2C, 0x8B, 0x5A, 0x9D, 0x7E, 0x6F } };

// VolumeController class implementation
VolumeController::VolumeController()
    : m_isInitialized(false)
    , m_deviceEnumerator(nullptr)
    , m_defaultDevice(nullptr)
    , m_endpointVolume(nullptr)
    , m_currentDeviceId()
    , m_captureDevice(nullptr)
    , m_captureEndpointVolume(nullptr)
    , m_currentCaptureDeviceId()
{
}

VolumeController::~VolumeController()
{
    cleanup();
}

bool VolumeController::initialize()
{
    if (m_isInitialized) {
        return true; // Already initialized
    }

    HRESULT hr;

    // Create device enumerator
    hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
                          __uuidof(IMMDeviceEnumerator),
                          reinterpret_cast<void**>(&m_deviceEnumerator));
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        qDebug() << "[VolumeController::initialize] Failed to create device enumerator, hr =" << QString::number(hr, 16);
#endif
        return false;
    }

    // Get default audio endpoint and activate volume interface
    if (!refreshAudioEndpoint(VOLUME_DEVICE_TYPE_PLAYBACK)) {
        cleanup();
        return false;
    }

    // Get default capture endpoint and activate volume interface
    if (!refreshAudioEndpoint(VOLUME_DEVICE_TYPE_CAPTURE)) {
        // Capture device initialization failure should not prevent overall initialization
        // as playback is the primary function
#ifdef DEBUG_LOGOUT_ON
        qDebug() << "[VolumeController::initialize] Warning: Failed to initialize capture device, but playback device initialized successfully";
#endif
    }

    m_isInitialized = true;

#ifdef DEBUG_LOGOUT_ON
    qDebug() << "[VolumeController::initialize] Volume controller initialized successfully";
#endif
    return true;
}

void VolumeController::cleanup()
{
    if (m_endpointVolume) {
        m_endpointVolume->Release();
        m_endpointVolume = nullptr;
    }

    if (m_defaultDevice) {
        m_defaultDevice->Release();
        m_defaultDevice = nullptr;
    }

    if (m_captureEndpointVolume) {
        m_captureEndpointVolume->Release();
        m_captureEndpointVolume = nullptr;
    }

    if (m_captureDevice) {
        m_captureDevice->Release();
        m_captureDevice = nullptr;
    }

    if (m_deviceEnumerator) {
        m_deviceEnumerator->Release();
        m_deviceEnumerator = nullptr;
    }

    m_isInitialized = false;
    m_currentDeviceId.clear();
    m_currentCaptureDeviceId.clear();

#ifdef DEBUG_LOGOUT_ON
    qDebug() << "[VolumeController::cleanup] Volume controller cleaned up";
#endif
}

bool VolumeController::refreshAudioEndpoint(int deviceType)
{
    if (!m_deviceEnumerator) {
        return false;
    }

    HRESULT hr;
    IMMDevice* newDevice = nullptr;
    EDataFlow dataFlow = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? eCapture : eRender;

    // Get default audio endpoint (render for playback, capture for microphone)
    hr = m_deviceEnumerator->GetDefaultAudioEndpoint(dataFlow, eConsole, &newDevice);
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::refreshAudioEndpoint]" << deviceTypeStr << "- Failed to get default audio endpoint, hr =" << QString::number(hr, 16);
#endif
        return false;
    }

    // Get device ID to track current device
    LPWSTR deviceId = nullptr;
    hr = newDevice->GetId(&deviceId);
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::refreshAudioEndpoint]" << deviceTypeStr << "- Failed to get device ID, hr =" << QString::number(hr, 16);
#endif
        newDevice->Release();
        return false;
    }

    // Store current device ID based on device type
    if (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) {
        m_currentCaptureDeviceId = deviceId;
    } else {
        m_currentDeviceId = deviceId;
    }
    CoTaskMemFree(deviceId);

    // Release old device and endpoint volume if exists
    if (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) {
        if (m_captureDevice) {
            m_captureDevice->Release();
            m_captureDevice = nullptr;
        }
        if (m_captureEndpointVolume) {
            m_captureEndpointVolume->Release();
            m_captureEndpointVolume = nullptr;
        }
        // Store new capture device
        m_captureDevice = newDevice;
    } else {
        if (m_defaultDevice) {
            m_defaultDevice->Release();
            m_defaultDevice = nullptr;
        }
        if (m_endpointVolume) {
            m_endpointVolume->Release();
            m_endpointVolume = nullptr;
        }
        // Store new playback device
        m_defaultDevice = newDevice;
    }

    // Get audio endpoint volume interface
    IAudioEndpointVolume* endpointVolume = nullptr;
    IMMDevice* targetDevice = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureDevice : m_defaultDevice;
    hr = targetDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL,
                                nullptr, reinterpret_cast<void**>(&endpointVolume));
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::refreshAudioEndpoint]" << deviceTypeStr << "- Failed to activate audio endpoint volume, hr =" << QString::number(hr, 16);
#endif
        return false;
    }

    // Store endpoint volume interface based on device type
    if (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) {
        m_captureEndpointVolume = endpointVolume;
    } else {
        m_endpointVolume = endpointVolume;
    }

#ifdef DEBUG_LOGOUT_ON
    const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
    std::wstring currentId = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_currentCaptureDeviceId : m_currentDeviceId;
    qDebug() << "[VolumeController::refreshAudioEndpoint]" << deviceTypeStr << "- Audio endpoint refreshed successfully, Device ID:" << QString::fromStdWString(currentId);
#endif
    return true;
}

bool VolumeController::updateDefaultAudioEndpointIfChanged(int deviceType)
{
    if (!m_deviceEnumerator) {
        return false;
    }

    HRESULT hr;
    IMMDevice* currentDevice = nullptr;
    EDataFlow dataFlow = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? eCapture : eRender;

    // Get current default audio endpoint
    hr = m_deviceEnumerator->GetDefaultAudioEndpoint(dataFlow, eConsole, &currentDevice);
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::updateDefaultAudioEndpointIfChanged]" << deviceTypeStr << "- Failed to get current default audio endpoint, hr =" << QString::number(hr, 16);
#endif
        return false;
    }

    // Get current device ID
    LPWSTR deviceId = nullptr;
    hr = currentDevice->GetId(&deviceId);
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::updateDefaultAudioEndpointIfChanged]" << deviceTypeStr << "- Failed to get current device ID, hr =" << QString::number(hr, 16);
#endif
        currentDevice->Release();
        return false;
    }

    std::wstring currentDeviceId = deviceId;
    CoTaskMemFree(deviceId);
    currentDevice->Release();

    // Check if device has changed
    std::wstring storedDeviceId = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_currentCaptureDeviceId : m_currentDeviceId;
    if (currentDeviceId != storedDeviceId) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::updateDefaultAudioEndpointIfChanged]" << deviceTypeStr << "- Default audio device changed, refreshing endpoint";
#endif
        // Device changed, refresh endpoint
        return refreshAudioEndpoint(deviceType);
    }

    // Device hasn't changed, no update needed
    return true;
}

bool VolumeController::setVolume(float volumePercentage, int deviceType)
{
    IAudioEndpointVolume* endpointVol = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureEndpointVolume : m_endpointVolume;

    if (!m_isInitialized || !endpointVol) {
        return false;
    }

    // Check if default audio device has changed and update if necessary
    if (!updateDefaultAudioEndpointIfChanged(deviceType)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::setVolume]" << deviceTypeStr << "- Failed to update audio endpoint";
#endif
        return false;
    }

    // Refresh endpoint volume pointer after update
    endpointVol = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureEndpointVolume : m_endpointVolume;

    float clampedPercentage = clampVolumePercentage(volumePercentage);
    float scalar = percentageToScalar(clampedPercentage);

    // Set the volume level first
    // Use QKeyMapper-specific GUID to identify this volume change event
    // This allows other applications to distinguish between QKeyMapper volume changes
    // and system/user-initiated changes, but does NOT control Windows volume OSD display
    HRESULT hr = endpointVol->SetMasterVolumeLevelScalar(scalar, &s_QKeyMapperVolumeGUID);
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::setVolume]" << deviceTypeStr << "- Failed to set volume to" << clampedPercentage << "%, hr =" << QString::number(hr, 16);
#endif
        return false;
    }

    // Apply Windows-like mute logic: mute at 0%, unmute at non-zero
    if (!applyWindowsMuteLogic(clampedPercentage, deviceType)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::setVolume]" << deviceTypeStr << "- Failed to apply mute logic for volume" << clampedPercentage << "%";
#endif
        // Don't return false here as volume was set successfully, mute operation is supplementary
    }

#ifdef DEBUG_LOGOUT_ON
    const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
    qDebug() << "[VolumeController::setVolume]" << deviceTypeStr << "- Volume set to" << clampedPercentage << "%";
#endif
    return true;
}

float VolumeController::getCurrentVolume(int deviceType)
{
    IAudioEndpointVolume* endpointVol = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureEndpointVolume : m_endpointVolume;

    if (!m_isInitialized || !endpointVol) {
        return 0.0f;
    }

    // Check if default audio device has changed and update if necessary
    if (!updateDefaultAudioEndpointIfChanged(deviceType)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::getCurrentVolume]" << deviceTypeStr << "- Failed to update audio endpoint";
#endif
        return 0.0f;
    }

    // Refresh endpoint volume pointer after update
    endpointVol = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureEndpointVolume : m_endpointVolume;

    float scalar = 0.0f;
    HRESULT hr = endpointVol->GetMasterVolumeLevelScalar(&scalar);
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::getCurrentVolume]" << deviceTypeStr << "- Failed to get current volume, hr =" << QString::number(hr, 16);
#endif
        return 0.0f;
    }

    return scalarToPercentage(scalar);
}

bool VolumeController::adjustVolume(float deltaPercentage, int deviceType)
{
    float currentVolume = getCurrentVolume(deviceType);
    if (currentVolume < 0.0f) {
        return false; // Failed to get current volume
    }

    float newVolume = currentVolume + deltaPercentage;
    return setVolume(newVolume, deviceType);
}

float VolumeController::percentageToScalar(float percentage)
{
    return percentage / 100.0f;
}

float VolumeController::scalarToPercentage(float scalar)
{
    float percentage = scalar * 100.0f;

    // Round to specified decimal precision (e.g., 2 decimal places: 0.01)
    float precision = std::pow(10.0f, static_cast<float>(VOLUME_DECIMAL_PRECISION));
    return std::round(percentage * precision) / precision;
}

float VolumeController::clampVolumePercentage(float percentage)
{
    if (percentage < VOLUME_MIN_PERCENTAGE) {
        percentage = VOLUME_MIN_PERCENTAGE;
    }
    else if (percentage > VOLUME_MAX_PERCENTAGE) {
        percentage = VOLUME_MAX_PERCENTAGE;
    }

    // Round to specified decimal precision (e.g., 2 decimal places: 0.01)
    float precision = std::pow(10.0f, static_cast<float>(VOLUME_DECIMAL_PRECISION));
    return std::round(percentage * precision) / precision;
}

bool VolumeController::setMute(bool muted, int deviceType)
{
    IAudioEndpointVolume* endpointVol = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureEndpointVolume : m_endpointVolume;

    if (!m_isInitialized || !endpointVol) {
        return false;
    }

    // Check if default audio device has changed and update if necessary
    if (!updateDefaultAudioEndpointIfChanged(deviceType)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::setMute]" << deviceTypeStr << "- Failed to update audio endpoint";
#endif
        return false;
    }

    // Refresh endpoint volume pointer after update
    endpointVol = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureEndpointVolume : m_endpointVolume;

    // Use QKeyMapper-specific GUID to identify this mute change event
    HRESULT hr = endpointVol->SetMute(muted ? TRUE : FALSE, &s_QKeyMapperVolumeGUID);
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::setMute]" << deviceTypeStr << "- Failed to set mute state to" << muted << ", hr =" << QString::number(hr, 16);
#endif
        return false;
    }

#ifdef DEBUG_LOGOUT_ON
    const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
    qDebug() << "[VolumeController::setMute]" << deviceTypeStr << "- Mute state set to" << muted;
#endif
    return true;
}

bool VolumeController::isMuted(int deviceType)
{
    IAudioEndpointVolume* endpointVol = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureEndpointVolume : m_endpointVolume;

    if (!m_isInitialized || !endpointVol) {
        return false;
    }

    // Check if default audio device has changed and update if necessary
    if (!updateDefaultAudioEndpointIfChanged(deviceType)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::isMuted]" << deviceTypeStr << "- Failed to update audio endpoint";
#endif
        return false;
    }

    // Refresh endpoint volume pointer after update
    endpointVol = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureEndpointVolume : m_endpointVolume;

    BOOL muted = FALSE;
    HRESULT hr = endpointVol->GetMute(&muted);
    if (FAILED(hr)) {
#ifdef DEBUG_LOGOUT_ON
        const char* deviceTypeStr = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? "Capture" : "Playback";
        qDebug() << "[VolumeController::isMuted]" << deviceTypeStr << "- Failed to get mute state, hr =" << QString::number(hr, 16);
#endif
        return false;
    }

    return (muted == TRUE);
}

bool VolumeController::applyWindowsMuteLogic(float volumePercentage, int deviceType)
{
    IAudioEndpointVolume* endpointVol = (deviceType == VOLUME_DEVICE_TYPE_CAPTURE) ? m_captureEndpointVolume : m_endpointVolume;

    if (!m_isInitialized || !endpointVol) {
        return false;
    }

    // Apply Windows-like mute logic:
    // - Setting volume to 0% should mute the system
    // - Setting volume to any non-zero value should unmute the system
    bool shouldMute = (volumePercentage <= VOLUME_EPSILON);

    return setMute(shouldMute, deviceType);
}
